/***************************************************************************
 * The Open Armory API is a project to retrieve World of Warcraft(R) 
 * related XML data from The Armory.
 * 
 * The data retrieved via the Open Armory API from The Armory is 
 * proprietary to Blizzard Entertainment, Inc(R).
 * *************************************************************************
 * Copyright (C) 2009 - 2010 Tedla M.B. Brandsema
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 ***************************************************************************/
package net.openarmory.core.domain.url;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Properties;

import net.openarmory.core.config.ConfigurationProperties;

import org.apache.log4j.Logger;

// TODO: Auto-generated Javadoc
/**
 * <p>
 * The Class <b>URLConnectionReader</b> is built around a single method for
 * connecting to a specified URL and retrieving it's contents.
 * </p>
 */
public class UrlConnectionReader {

	/** The log. */
	private static final Logger LOG = Logger
			.getLogger(UrlConnectionReader.class);

	/** The Constant INTERNAL_SERVER_ERROR. */
	private static final int INTERNAL_SERVER_ERROR = 500;

	/** The Constant SERVICE_UNAVAILABLE. */
	private static final int SERVICE_UNAVAILABLE = 503;

	/** The Constant TIMEOUT. */
	private static final long TIMEOUT = 3000l;

	/** The Constant SECOND_IN_MILISECONDS. */
	private static final int SECOND_IN_MILISECONDS = 1000;

	/** The Constant RECONNECT_ATTEMPTS. */
	private static final int RECONNECT_ATTEMPTS = 10;

	/** The RECONNEC t_ attempts. */
	private int reconnectAttempts = RECONNECT_ATTEMPTS;

	/** The response code. */
	private int responseCode;

	/** The content type header. */
	private String contentTypeHeader;

	/** The content encoding header. */
	private String contentEncodingHeader;

	/** The content length header. */
	private int contentLengthHeader;

	/** The config. */
	private ConfigurationProperties config;

	/**
	 * Gets the response code.
	 * 
	 * @return the response code
	 */
	public final int getResponseCode() {
		return this.responseCode;
	}

	/**
	 * Gets the content type header.
	 * 
	 * @return the content type header
	 */
	public final String getContentTypeHeader() {
		return this.contentTypeHeader;
	}

	/**
	 * Gets the content encoding header.
	 * 
	 * @return the content encoding header
	 */
	public final String getContentEncodingHeader() {
		return this.contentEncodingHeader;
	}

	/**
	 * Gets the content length header.
	 * 
	 * @return the content length header
	 */
	public final int getContentLengthHeader() {
		return this.contentLengthHeader;
	}

	/**
	 * Instantiates a new URL connection reader.
	 */
	public UrlConnectionReader() {
		try {
			this.config = ConfigurationProperties.getInstance();
		} catch (final IOException e) {
			LOG.error("Could not open configuration file.", e);
		}
	}

	/**
	 * This method connects to the specified URL and returns it's response to an
	 * input stream.
	 * 
	 * @param url
	 *            The URL to connect to.
	 * 
	 * @return the input stream
	 */
	public final InputStream fetchContent(final URL url) {
		try {
			final Properties systemProperties = System.getProperties();
			if (this.config.isProxy()) {
				systemProperties.setProperty("http.proxyHost",
						this.config.getProxyHost());
				systemProperties.setProperty("http.proxyPort",
						this.config.getProxyPort());
			}
			systemProperties.setProperty("http.agent",
					this.config.getUserAgent());

			final HttpURLConnection connection = (HttpURLConnection) url
					.openConnection();

			/*
			 * Set class properties
			 */
			this.responseCode = connection.getResponseCode();
			this.contentTypeHeader = connection.getContentType();
			this.contentEncodingHeader = connection.getContentEncoding();
			this.contentLengthHeader = connection.getContentLength();
			if (((this.responseCode == INTERNAL_SERVER_ERROR) || (this.responseCode == SERVICE_UNAVAILABLE))
					&& (this.reconnectAttempts > 0)) {
				this.reconnectAttempts--;
				LOG.info("Server returned response code: " + this.responseCode
						+ ". " + "Going to wait "
						+ (TIMEOUT / SECOND_IN_MILISECONDS)
						+ " seconds  before atempting to reconnect. "
						+ "Attempts remaining: " + this.reconnectAttempts
						+ "\n");
				Thread.sleep(TIMEOUT);

				return fetchContent(url);
			} else {
				this.reconnectAttempts = RECONNECT_ATTEMPTS;
				connection.connect();
				LOG.info("Fetching InputStream from URL: " + url.toString());
				return connection.getInputStream();
			}

		} catch (final InterruptedException e) {
			LOG.error("Current thread was interupted.", e);
		} catch (final IOException e) {
			LOG.error("Could not open url: " + url.toString(), e);
		}

		return null;
	}

}
